#include <tinx/io.h>
#include <tinx/stdlib.h>
#include <tinx/time.h>
#include <tinx/assert.h>

#define CMOS_ADDR 0x70 // CMOS 地址寄存器
#define CMOS_DATA 0x71 // CMOS 数据寄存器

// 下面是 CMOS 信息的寄存器索引
#define CMOS_SECOND 0x00  // (0 ~ 59)
#define CMOS_MINUTE 0x02  // (0 ~ 59)
#define CMOS_HOUR 0x04    // (0 ~ 23)
#define CMOS_WEEKDAY 0x06 // (1 ~ 7) 星期天 = 1，星期六 = 7
#define CMOS_DAY 0x07     // (1 ~ 31)
#define CMOS_MONTH 0x08   // (1 ~ 12)
#define CMOS_YEAR 0x09    // (0 ~ 99)
#define CMOS_CENTURY 0x32 // 可能不存在
#define CMOS_NMI 0x80

#define CMOS_A 0x0a
#define CMOS_B 0x0b
#define CMOS_C 0x0c
#define CMOS_D 0x0d
#define CMOS_NMI 0x80

#define MINUTE (60)
#define HOUR (MINUTE * 60)
#define DAY (HOUR * 24)
#define YEAR (DAY * 365)

static int month[12] = {
    0,
    31,
    31 + 29,
    31 + 29 + 31,
    31 + 29 + 31 + 30,
    31 + 29 + 31 + 30 + 31,
    31 + 29 + 31 + 30 + 31 + 30,
    31 + 29 + 31 + 30 + 31 + 30 + 31,
    31 + 29 + 31 + 30 + 31 + 30 + 31 + 31,
    31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
    31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
    31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
};

// 读 cmos 寄存器的值
u8 cmos_read(u8 addr)
{
    outb(CMOS_ADDR, CMOS_NMI | addr);
    return inb(CMOS_DATA);
};

// 写 cmos 寄存器的值
void cmos_write(u8 addr, u8 value)
{
    outb(CMOS_ADDR, CMOS_NMI | addr);
    outb(CMOS_DATA, value);
}

void time_read_bcd(tm_t *tm)
{
    do
    {
        tm->sec = cmos_read(CMOS_SECOND);
        tm->min = cmos_read(CMOS_MINUTE);
        tm->hour = cmos_read(CMOS_HOUR);
        tm->wday = cmos_read(CMOS_WEEKDAY);
        tm->mday = cmos_read(CMOS_DAY);
        tm->mon = cmos_read(CMOS_MONTH);
        tm->year = cmos_read(CMOS_YEAR);
    } while (tm->sec != cmos_read(CMOS_SECOND));
}

void time_read(tm_t *tm)
{
    tm->sec = bcd_to_bin(tm->sec);
    tm->min = bcd_to_bin(tm->min);
    tm->hour = bcd_to_bin(tm->hour);
    tm->mday = bcd_to_bin(tm->mday);
    tm->wday = bcd_to_bin(tm->wday);
    tm->mon = bcd_to_bin(tm->mon);
    tm->year = bcd_to_bin(tm->year);
    tm->isdst = -1;
}

time_t mktime(tm_t *tm)
{
    int year;

    if (tm->year >= 70)
        year = tm->year - 70;
    else
        year = tm->year - 70 + 100;

    time_t res = year * YEAR + ((year + 1) / 4) * DAY;
    res += month[tm->mon] * DAY;
    if (tm->mon > 2 && ((year + 2) % 4))
        res -= DAY;

    res += (tm->mday - 1) * DAY;
    res += tm->hour * HOUR;
    res += tm->min * MINUTE;
    res += tm->sec;

    return res;
}

time_t startup_time;
extern volatile u32 jiffies;
extern u32 jiffy;

time_t sys_time()
{
    return startup_time + (jiffies * jiffy) / 1000;
}

time_t ktime()
{
    sys_time();
}

// 一种高级的获得时间的方式
int sys_clock_gettime(int clockid, timespec_t *ts)
{
    switch (clockid)
    {
    case CLOCK_MONOTONIC:
    case CLOCK_MONOTONIC_COARSE:
    case CLOCK_MONOTONIC_RAW:
    case CLOCK_REALTIME:
        ts->sec = sys_time();
        ts->nsec = 0;
        return EOK;
    default:
        panic("unknow clock id %d", clockid);
    }
}

int sys_clock_gettime64(int clockid, timespec64_t *ts)
{
    switch (clockid)
    {
    case CLOCK_MONOTONIC:
    case CLOCK_MONOTONIC_COARSE:
    case CLOCK_MONOTONIC_RAW:
    case CLOCK_REALTIME:
        ts->sec = sys_time();
        ts->nsec = 0;
        return EOK;
    default:
        panic("unknow clock id %d", clockid);
    }
}

void time_init()
{
    tm_t tm;
    time_read(&tm);
    startup_time = mktime(&tm);
}